<?php
/**
 * Created by PhpStorm.
 * User: Jason
 * Date: 2017/8/27
 * Time: 15:59
 */

namespace Api\Service;

use Think\Log;
class WxOrderService
{
    protected $appid;
    protected $mch_id;
    protected $key;
    protected $notify_url;
    public function __construct()
    {
        $this->appid = C('WECHAT.appid');
        $this->mch_id = C('WECHAT.mch_id');
        $this->key = C('WECHAT.key');
        $this->notify_url = C('WECHAT.notify_url');
    }

    public function validateParam($param)
    {
        if(empty($param['id'])) {
            E('id不能为空！');
        }
        if(empty($param['uid'])) {
            E('uid不能为空！');
        }
    }

    /**
     * 生成预支付订单
     */
    public function unifiedorder($param)
    {
        //获取用户选择的充值方案
        $coinModel = M('UserCoinCharge');
        $coinCharge = $coinModel->where(array('id'=>$param['id']))->find();
        $body = '多谱-'.$coinCharge['name'];
        $totalFee = (float)$coinCharge['discount_price'] * 100;
        $coin = (int)$coinCharge['amount'] + (int)$coinCharge['additional_coin'];
        $attach = 'coin=' . $coin . '&style=' . $param['style'];
        $deviceInfo = $param['device_info'] ? $param['device_info'] : 'WEB';
        $spbillCreateIp = $_SERVER['REMOTE_ADDR'];  //$_SERVER['REMOTE_ADDR']  本地测试时改为127.0.0.1
        //获取用户openid;
        $userInfo = M('User')->where(array('id'=>$param['uid']))->field('open_id')->select();
        $openid = $userInfo[0]['open_id'];
        $parameters = array(
            'appid' => $this->appid,
            'attach' => $attach,
            'body' => $body,
            'device_info' => $deviceInfo,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->getNonceStr(),
            'notify_url' => $this->notify_url,
            'openid' => $openid,
            'out_trade_no' => $this->outTradeNo(),
            'spbill_create_ip' => $spbillCreateIp,
            'total_fee' => $totalFee,
            'trade_type' => 'JSAPI',
        );
        $parameters['sign'] = $this->sign($parameters);

        //记录订单
        $model = M('UserRechargeOrder');
        $data = array(
            'user_id' => $param['uid'],
            'set_id' => $param['id'],
            'detail' => explode('-', $body)[1],
            'order_no' => $parameters['out_trade_no'],
            'total_fee' => (float)$parameters['total_fee'] / 100,
            'create_time' => date('Y-m-d H:i:s', time())
        );
        $model->add($data);

        return $parameters;
    }

    /**
     * 检验预支付订单返回结果是否有误
     * @param $returnData
     * @return mixed
     */
    public function decryptData($returnData)
    {
        //step-1 : 将xml数组转为array
        $data = $this->xmlToArray($returnData);

        //step-2: 检验返回结果
        if ($data['return_code'] == 'FAIL') {
            E($data['return_msg']);
        }
        if ($data['result_code'] == 'FAIL') {
            E($data['err_code']);
        } else {
            return $data;
        }
    }

    /**
     * 生成支付api的请求参数
     * @param $prepay_id 统一下单接口返回的 prepay_id 参数值
     */
    public function wxPayment($prepay_id)
    {
        $parameters = array(
            'appId' => $this->appid,
            'timeStamp' => time(),
            'nonceStr' => $this->getNonceStr(),
            'package' => 'prepay_id='.$prepay_id,
            'signType' => 'MD5',
        );
        $parameters['paySign'] = $this->sign($parameters);
        return $parameters;
    }

    /**
     * 生成sign
     * @param $parameters
     * @return string
     */
    public function sign($parameters)
    {
        //step-1 : 按字典序排序参数
        ksort($parameters);
        $stringSignTemp = '';
        foreach ($parameters as $k => $v) {
            $stringSignTemp .= $k . "=" . $v . "&";
        }
        $stringSignTemp = substr($stringSignTemp, 0, strlen($stringSignTemp)-1);
        //step-2 : 在string后加入key
        $stringSignTemp = $stringSignTemp . "&key=" . $this->key;
        //step-3 : md5加密并转为大写
        $sign = mb_strtoupper(md5($stringSignTemp));

        return $sign;
    }


    /**
     * 数组转换成XML
     * @param $arr
     * @return string
     */
    public function arrayToXml($arr)
    {
        $xml = "<root>";
        foreach ($arr as $key => $val) {
            if (is_array($val)) {
                $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            }
        }
        $xml .= "</root>";
        return $xml;
    }

    //xml转换成数组
    public function xmlToArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);
        return $val;
    }

    /**
     * 更新订单状态
     */
    public function updateOrder($param)
    {
        $map = array(
            'order_no' => $param['out_trade_no'],
        );
        $model = M('UserRechargeOrder');
        $order = $model->where($map)->find();
        if (!empty($order)) {
            //验证订单状态

            if ($order[0]['pay_status'] == 1) {
                Log::writeInfo('The order: No.'. json_encode($order[0]['order_no']) . 'is already processed!', 'wxReply');
                return true;
            }
            $order_total_fee = $param['total_fee'] /100;
            if ((int)$order_total_fee != (int)$order['total_fee']) {
                Log::writeInfo('order: No.'. json_encode($order[0]['order_no']) . '\'s total_fee dismatch. pay_fee is '.json_encode($order_total_fee).'.order_fee is '.json_encode($order[0]['total_fee']), 'wxReply');
                return 0;
            }
            Log::writeInfo('order: No.'. json_encode($order[0]['order_no']) . '\'s total_fee match, modify pay_status from 0 to 1', 'wxReply');
            $data = array(
                'pay_status' => 1, //修改订单状态为已支付
                'pay_time' => $param['time_end'],
                'cash_fee' => $param['cash_fee'],
                'transaction_id' => $param['transaction_id']
            );
            $id = $model->where($map)->save($data);
            //增加用户帐户金币数量
            $coin = intval(substr($param['attach'], 5, strpos($param['attach'], 'style')-6));  //获取金币数
            $style = intval(substr($param['attach'], -1));
            $userMemberService = new UserMemberService();
            $userMemberService->rechargeUserAccount($order['user_id'], $coin, $style);
            //更新用户金币流水
            $userMemberService->updateUserPayment($order['user_id'], $data['attach'], 1, $data['time_end'], 100, $data['out_trade_no']);
            return $id;
        } else {
            return false;
        }

    }

    /**
     * 产生随机字符串
     */
    private function getNonceStr()
    {
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $noceStr = "";
        for ($i = 0; $i < 32; $i++) {
            $noceStr .= $chars[mt_rand( 0, strlen( $chars ) - 1 )];
        }
        return $noceStr;
    }

    /**
     * 生成商品订单号
     */
    private function outTradeNo()
    {
        $date = date('YmdHis', time());
        $chars = '0123456789';
        $rand = "";
        for ($i = 0; $i < 6; $i++) {
            $rand .= $chars[mt_rand( 0, strlen( $chars ) - 1 )];
        }
        $outTradeNo = $date . $rand;
        return $outTradeNo;
    }
}